package com.zmn.plat.business.impl.standard;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import com.alibaba.dubbo.config.annotation.Reference;
import com.google.common.collect.Maps;
import com.zmn.common.constant.GlobalConsts;
import com.zmn.common.dto2.ResponseDTO;
import com.zmn.common.utils.office.excel.ExportExcel;
import com.zmn.mcc.cas.model.LoginStaff;
import com.zmn.mcc.common.dto.dept.DeptDRO;
import com.zmn.mcc.common.dto.organize.OrganizeDRO;
import com.zmn.mcc.dubbo.interfaces.dept.DeptListRemoteService;
import com.zmn.mcc.dubbo.interfaces.organize.OrganizeListRemoteService;
import com.zmn.plat.business.interfaces.standard.StandardRecordBService;
import com.zmn.plat.common.dictionary.StandardCategoryEnum;
import com.zmn.plat.common.dictionary.StandardPermissionAppOrganEnum;
import com.zmn.plat.dubbo.utils.DubboConsts;
import com.zmn.plat.model.entity.standard.record.StandardRecord;
import com.zmn.plat.model.entity.standard.record.StandardRecordQuery;
import com.zmn.plat.model.vo.standard.record.StandardRecordExportExcelVO;
import com.zmn.plat.services.interfaces.standard.record.StandardRecordService;

import lombok.extern.slf4j.Slf4j;

/**
 * 描述: 服务类型业务实现类接口
 *
 * @author wangjie
 * @since 16:40 2019/5/20
 **/
@Slf4j
@Service
public class StandardRecordBServiceImpl implements StandardRecordBService {

    private Logger logger = LoggerFactory.getLogger(StandardRecordBServiceImpl.class);

    @Resource
    protected StandardRecordService standardRecordService;

    @Reference(version = DubboConsts.INTERFACE_VERSION, check = false)
    protected DeptListRemoteService deptListRemoteService;

    @Reference(version = DubboConsts.INTERFACE_VERSION, check = false)
    protected OrganizeListRemoteService organizeListRemoteService;

    @Override
    public ExportExcel<StandardRecordExportExcelVO> exportExcel(StandardRecordQuery query, LoginStaff staff) {
        List<Integer> appList = Arrays.asList(StandardPermissionAppOrganEnum.YEYXAPP.getCode(), StandardPermissionAppOrganEnum.ZMNAPP.getCode(), StandardPermissionAppOrganEnum.YBWXAPP.getCode(),
            StandardPermissionAppOrganEnum.CNHBAPP.getCode());
        query.setStaffId(staff.getStaffId());
        query.setCreater(staff.getRealName());
        List<StandardRecordExportExcelVO> standardRecordExportExcelVOS = standardRecordService.listExportExcelPageByQuery(query);

        List<Integer> deptOneIds = standardRecordExportExcelVOS.stream().map(e -> e.getDeptOneId()).distinct().collect(Collectors.toList());
        List<Integer> deptTwoIds = standardRecordExportExcelVOS.stream().map(e -> e.getDeptTwoId()).distinct().collect(Collectors.toList());
        List<Integer> allDeptIds = new ArrayList<>();
        allDeptIds.addAll(deptOneIds);
        allDeptIds.addAll(deptTwoIds);

        ResponseDTO<List<DeptDRO>> responseDTO = deptListRemoteService.listDeptByLevelAndStatus(null, null);
        List<DeptDRO> allDepts = new ArrayList<>();
        if (responseDTO.isSuccess()) {
            allDepts = responseDTO.getData();
        }

        List<Integer> allOrganizeIds = allDepts.stream().map(e -> e.getOrgId()).distinct().collect(Collectors.toList());
        Map<Integer, DeptDRO> deptMap = allDepts.stream().collect(Collectors.toMap(item -> item.getDeptId(), item -> item, (p, u) -> p));

        ResponseDTO<List<OrganizeDRO>> organResponseDTO = organizeListRemoteService.listByLevel(null, null);
        List<OrganizeDRO> allOrgans = new ArrayList<>();
        if (organResponseDTO.isSuccess()) {
            allOrgans = organResponseDTO.getData();
        }

        Map<Integer, OrganizeDRO> organMap = allOrgans.stream().collect(Collectors.toMap(item -> item.getOrgId(), item -> item, (p, u) -> p));

        if (CollectionUtils.isEmpty(standardRecordExportExcelVOS)) {
            standardRecordExportExcelVOS = new ArrayList<>();
            standardRecordExportExcelVOS.add(new StandardRecordExportExcelVO());
        } else {
            standardRecordExportExcelVOS.forEach(e -> {
                e.setCategoryName(StandardCategoryEnum.of(e.getCategory()).getName());
                e.setStatusName(e.getStatus() == GlobalConsts.YES ? "启用" : "停用");
                e.setPublishDeptName(e.getDeptOneName() + "/" + e.getDeptTwoName());
                e.setMustReadName(e.getMustRead() == GlobalConsts.YES ? "是" : "否");
                if (e.getOrgId() != null) {
                    if (appList.contains(e.getOrgId())) {
                        if (Objects.equals(StandardPermissionAppOrganEnum.YEYXAPP.getCode(), e.getOrgId())) {
                            e.setPermissionDept(StandardPermissionAppOrganEnum.YEYXAPP.getName());
                        } else if (Objects.equals(StandardPermissionAppOrganEnum.ZMNAPP.getCode(), e.getOrgId())) {
                            e.setPermissionDept(StandardPermissionAppOrganEnum.ZMNAPP.getName());
                        } else if (Objects.equals(StandardPermissionAppOrganEnum.YBWXAPP.getCode(), e.getOrgId())) {
                            e.setPermissionDept(StandardPermissionAppOrganEnum.YBWXAPP.getName());
                        } else if (Objects.equals(StandardPermissionAppOrganEnum.CNHBAPP.getCode(), e.getOrgId())) {
                            e.setPermissionDept(StandardPermissionAppOrganEnum.CNHBAPP.getName());
                        }
                    } else {
                        if (Objects.equals(e.getOrgFlag(), 1)) {
                            List<OrganizeDRO> organList = listParentOrgTreeByOrgId(organMap, e.getOrgId());
                            Collections.reverse(organList);
                            String str = organList.stream().filter(item -> item != null).map(n -> n.getOrgName()).collect(Collectors.joining("/", "", ""));
                            e.setPermissionDept(str);
                        } else {
                            DeptDRO dept = Optional.ofNullable(deptMap.get(e.getOrgId())).orElse(new DeptDRO());
                            List<OrganizeDRO> organList = listParentOrgTreeByOrgId(organMap, dept.getOrgId());
                            Collections.reverse(organList);
                            String orgStr = organList.stream().map(n -> n.getOrgName()).collect(Collectors.joining("/", "", ""));
                            List<DeptDRO> depts = listParentDeptTreeByDeptId(deptMap, e.getOrgId());
                            Collections.reverse(depts);
                            String deptStr = depts.stream().filter(item -> item != null).map(n -> n.getDeptName()).collect(Collectors.joining("/", "", ""));
                            String str = orgStr + "/" + deptStr;
                            e.setPermissionDept(str);
                        }
                    }
                }
            });
        }

        // 封装Excel对象
        LinkedHashMap<String, String> fieldMap = Maps.newLinkedHashMap();

        fieldMap.put("sort", "排序");
        fieldMap.put("standardId", "ID");
        fieldMap.put("title", "标题");
        fieldMap.put("version", "版本号");
        fieldMap.put("categoryName", "类别");
        fieldMap.put("creater", "作者");
        fieldMap.put("publishDeptName", "发布部门");
        fieldMap.put("statusName", "发布状态");
        fieldMap.put("mustReadName", "是否必须阅读");
        fieldMap.put("createTime", "创建时间");
        fieldMap.put("permissionDept", "阅读部门");

        ExportExcel<StandardRecordExportExcelVO> excel = new ExportExcel<>(standardRecordExportExcelVOS, fieldMap, true);

        return excel;
    }

    @Override
    public void downLoadFiles(HttpServletResponse response, Integer standardId) {
        List<String> urls = getFiles(standardId);

        if (!CollectionUtils.isEmpty(urls)) {
            downloadZip(response, urls, "附件");
        }
    }

    @Override
    public void downLoadFile(HttpServletResponse response, String url) {
        downloadFile(response, url);
    }

    @Override
    public Integer countFiles(Integer standardId) {
        return getFiles(standardId).size();
    }

    private List<String> getFiles(Integer standardId) {
        StandardRecord standardRecord = standardRecordService.findByKey(standardId);
        List<String> urls = new ArrayList<>();
        if (!StringUtils.isEmpty(standardRecord.getFileSrc())) {
            urls.add(standardRecord.getFileSrc());
        }
        if (!StringUtils.isEmpty(standardRecord.getFileSrc2())) {
            urls.add(standardRecord.getFileSrc2());
        }
        if (!StringUtils.isEmpty(standardRecord.getFileSrc3())) {
            urls.add(standardRecord.getFileSrc3());
        }
        if (!StringUtils.isEmpty(standardRecord.getFileSrc4())) {
            urls.add(standardRecord.getFileSrc4());
        }
        if (!StringUtils.isEmpty(standardRecord.getFileSrc5())) {
            urls.add(standardRecord.getFileSrc5());
        }

        return urls;
    }

    private void downloadFile(HttpServletResponse response, String urlFile) {
        String urlPath = urlFile;
        BufferedInputStream bin = null;
        HttpURLConnection httpURLConnection = null;
        OutputStream out = null;
        String[] params = urlPath.split("/");
        String fileName = "default.file";
        if (params != null && params.length > 0) {
            fileName = params[params.length - 1];
        }
        // 设置强制下载不打开
        response.setContentType("application/force-download");
        try {
            // 设置文件名
            response.addHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            log.error("不支持的编码格式", e);
        }
        try {
            // 统一资源
            URL url = new URL(urlPath);
            // 连接类的父类，抽象类
            URLConnection urlConnection = url.openConnection();
            // http的连接类
            httpURLConnection = (HttpURLConnection)urlConnection;
            // 设置超时
            httpURLConnection.setConnectTimeout(1000 * 5);
            // 设置请求方式，默认是GET
            httpURLConnection.setRequestMethod("GET");
            // 设置字符编码
            httpURLConnection.setRequestProperty("Charset", "UTF-8");
            // 打开到此 URL引用的资源的通信链接（如果尚未建立这样的连接）。
            httpURLConnection.connect();

            // 建立链接从请求中获取数据
            bin = new BufferedInputStream(httpURLConnection.getInputStream());

            out = response.getOutputStream();
            int size = 0;
            long len = 0;
            byte[] buf = new byte[2048];
            while ((size = bin.read(buf)) != -1) {
                len += size;
                out.write(buf, 0, size);
            }
            // 关闭资源
            bin.close();
            out.close();
            log.info("文件下载成功,文件大小：" + len / 1024 + "kb");
        } catch (MalformedURLException e) {

        } catch (IOException e) {
            log.error("文件下载失败！", e);
        } finally {
            if (bin != null) {
                try {
                    bin.close();
                } catch (IOException e) {
                    log.error("BufferedInputStream流关闭失败！", e);
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {

                    log.error("OutputStream流关闭失败！", e);
                }
            }
            if (httpURLConnection != null) {
                httpURLConnection.disconnect();
            }
        }
    }

    /**
     * 多文件打包下载
     *
     * @param response
     * @param paths
     *            文件路径集合
     * @param zipFileNameEn
     *            压缩文件名称
     */
    public void downloadZip(HttpServletResponse response, List<String> paths, String zipFileNameEn) {
        HttpURLConnection httpURLConnection = null;
        // 设置强制下载不打开
        response.setContentType("application/force-download");
        // 设置最终输出zip文件的目录+文件名
        SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
        String zipFileName = zipFileNameEn + formatter.format(new Date()) + ".zip";
        try {
            // 设置文件名
            response.addHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(zipFileName, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            log.error("不支持的编码格式", e);
        }

        ZipOutputStream zipStream = null;
        BufferedInputStream bufferStream = null;
        try {
            // 构造最终压缩包的输出流
            zipStream = new ZipOutputStream(response.getOutputStream());

            for (int i = 0; i < paths.size(); i++) {
                // 解码获取真实路径与文件名
                String[] params = paths.get(i).split("/");
                String fileName = "default.file";
                if (params != null && params.length > 0) {
                    fileName = params[params.length - 1];
                }

                // 统一资源
                URL url = new URL(paths.get(i));
                // 连接类的父类，抽象类
                URLConnection urlConnection = url.openConnection();
                // http的连接类
                httpURLConnection = (HttpURLConnection)urlConnection;
                // 设置超时
                httpURLConnection.setConnectTimeout(1000 * 5);
                // 设置请求方式，默认是GET
                httpURLConnection.setRequestMethod("GET");
                // 设置字符编码
                httpURLConnection.setRequestProperty("Charset", "UTF-8");
                // 打开到此 URL引用的资源的通信链接（如果尚未建立这样的连接）。
                httpURLConnection.connect();

                String realFileName = java.net.URLDecoder.decode(fileName, "UTF-8");
                /**
                 * 压缩条目不是具体独立的文件，而是压缩包文件列表中的列表项，称为条目，就像索引一样这里的name就是文件名, 文件名和之前的重复就会导致文件被覆盖
                 */
                ZipEntry zipEntry = new ZipEntry(realFileName);// 在压缩目录中文件的名字
                zipStream.putNextEntry(zipEntry);// 定位该压缩条目位置，开始写入文件到压缩包中
                // 建立链接从请求中获取数据
                bufferStream = new BufferedInputStream(httpURLConnection.getInputStream(), 1024 * 10);

                int read = 0;
                byte[] buf = new byte[1024 * 10];
                while ((read = bufferStream.read(buf, 0, 1024 * 10)) != -1) {
                    zipStream.write(buf, 0, read);
                }
            }
        } catch (Exception e) {
            log.info("下载出现错误:", e);
        } finally {
            // 关闭流
            try {
                if (null != bufferStream) {
                    bufferStream.close();
                }

                if (null != zipStream) {
                    zipStream.flush();
                    zipStream.close();
                }

                if (null != httpURLConnection) {
                    httpURLConnection.disconnect();
                }
            } catch (IOException e) {
                log.error("io流关闭异常", e);
            }
        }
    }

    private List<DeptDRO> listParentDeptTreeByDeptId(Map<Integer, DeptDRO> deptMap, Integer deptId) {
        List<DeptDRO> deptList = new ArrayList<>();
        return listParentDeptTreeByDeptId(deptMap, deptList, deptId);
    }

    private List<DeptDRO> listParentDeptTreeByDeptId(Map<Integer, DeptDRO> deptMap, List<DeptDRO> deptList, Integer deptId) {
        DeptDRO dept = deptMap.get(deptId);
        deptList.add(dept);
        if (dept == null || dept.getParentId() == null || Objects.equals(GlobalConsts.TOP_ID, dept.getParentId())) {
            return deptList;
        }
        return listParentDeptTreeByDeptId(deptMap, deptList, dept.getParentId());
    }

    private List<OrganizeDRO> listParentOrgTreeByOrgId(Map<Integer, OrganizeDRO> organMap, Integer orgId) {
        List<OrganizeDRO> organList = new ArrayList<>();
        return listParentOrgTreeByOrgId(organMap, organList, orgId);
    }

    private List<OrganizeDRO> listParentOrgTreeByOrgId(Map<Integer, OrganizeDRO> organMap, List<OrganizeDRO> organList, Integer orgId) {
        OrganizeDRO organ = organMap.get(orgId);
        organList.add(organ);
        if (organ == null || organ.getLevel() == null || Objects.equals(GlobalConsts.LEVEL_1, organ.getLevel())) {
            return organList;
        }
        return listParentOrgTreeByOrgId(organMap, organList, organ.getParentId());
    }
}
